为什么 Midscene 的 UI Agent 非得看见屏幕?
做 Midscene 的过程中,我经常遇到一个问题:为什么 UI Agent 一定要看截图?为什么不能继续沿用 DOM、selector、XPath、accessibility tree 这些传统自动化里已经很成熟的东西?
这个问题非常合理。过去十几年,UI 自动化基本就是沿着“结构化界面信息”这条路发展起来的。但如果我们要做的不是一个更聪明的 Web 测试框架,而是一个能操作 Web、移动端、桌面端、Canvas、自定义设备的 UI Agent,那么默认信息来源就得换一下:先看见屏幕,再决定怎么操作。
我们一开始也没有迷信纯视觉
先说结论:Midscene 不是因为“纯视觉听起来更 AI”才选择 pure-vision。
早期的 Midscene 同时尝试过 DOM 定位和视觉定位。DOM 方案在很多 Web 页面上当然有效,尤其是结构稳定、节点语义清晰、selector 可维护的页面。但在真实项目里,问题会不断冒出来:Canvas 里的控件、CSS background-image 里的视觉对象、跨域 iframe、Shadow DOM、虚拟列表、自绘组件、可访问性标注不完整的元素,都可能让 DOM 和用户看到的界面出现偏差。
这些偏差最麻烦的地方,不是某一次跑失败,而是它会把开发者拖进一种很低效的排障状态:到底是模型理解错了,还是 DOM 给错了,还是节点存在但用户看不见,还是页面视觉状态已经变了?
所以从 1.0 版本开始,Midscene 在 UI 操作和元素定位上走 pure-vision 路线:元素定位和交互主要基于截图完成。DOM 没有被彻底丢掉,它仍然可以在数据提取、页面理解、缓存优化等场景里发挥作用,但它不再是 UI 操作的默认依据。
这里不是要做姿态,而是一个很朴素的工程判断:UI Agent 要替用户操作,至少应该先看到用户看到的东西。
也就是屏幕。
DOM 是实现结构,屏幕是用户看到的结果
很多 Web 工程师天然相信 DOM。DOM 是可检索的、可编程的、可调试的,也是传统自动化最重要的基础设施。
但 DOM 不是 UI 本身。它只是 UI 的一种实现结构。
用户不会打开开发者工具去判断按钮在哪里。用户看到的是当前屏幕上的视觉对象:按钮、输入框、弹窗、遮罩、错误提示、选中态、禁用态、布局关系和上下文位置。
这两种视角很多时候是重合的,但不是总是重合。
DOM 里存在的节点,可能被遮挡、折叠、隐藏,用户根本看不到。屏幕上非常明确的控件,也可能在 DOM 里没有一个可用节点,比如 Canvas、地图、白板、图表、游戏界面、远程桌面和很多自绘控件。
一旦 UI Agent 默认相信 DOM,它理解的就不是“用户看到的界面”,而是“开发者工具里的界面”。这对传统 Web 自动化也许没问题,但对通用 UI Agent 来说,限制会很快出现。
因为 Agent 的目标不是遍历结构树,而是完成用户视角下的操作任务。
跨平台之后,截图最容易统一
如果只做 Web,DOM-first 是一个可以长期成立的选择。
但真实的软件使用场景远不止浏览器:Android、iOS、桌面应用、浏览器插件、远程桌面、车机屏幕、IoT 设备、企业内部工具、Canvas 或 WebGL 应用,都有自己的界面技术栈。
Web 有 DOM。Android 有 view hierarchy。iOS 有 accessibility tree。桌面端有窗口树和辅助功能接口。自绘界面甚至没有稳定的结构可读。
如果每个平台都要为 Agent 重新定义一套结构口径,那 Agent 永远会被平台绑住。
截图反而是更容易统一的输入。
只要能拿到屏幕画面,我们就能问同一组问题:当前界面是什么状态?用户能看到哪些可操作对象?目标元素在哪里?下一步应该点哪里、输入哪里、滚到哪里?操作之后有没有进入预期状态?
这也是 Midscene 选择截图作为核心输入的原因。底层设备适配当然仍然不同:Web、Android、iOS、桌面端的截图、点击、输入、滚动实现都不一样。但对上层 Agent 来说,它面对的是同一种输入形式:一个可见界面,以及一组可执行动作。
UI Agent 最难的不是点击,而是找到
在 UI 自动化里,点击、输入、滚动、按键这些动作本身并不复杂。
麻烦通常出在“找到”:
- 哪个是当前可用的提交按钮?
- 哪个输入框属于这一步流程?
- 列表里哪条记录是目标对象?
- 弹窗是否挡住了主流程?
- 按钮现在是禁用态还是可点击态?
- 页面是否真的跳转成功?
- 错误提示是在说用户输入错了,还是系统状态错了?
这些判断,人类几乎是瞬间完成的,因为人类直接看屏幕。
传统自动化要稳定完成这些判断,往往需要 selector、等待条件、业务断言、特殊分支、重试逻辑和大量兜底。UI Agent 的价值恰恰在这里:把过去依赖人眼判断的部分,变成模型可以处理的视觉理解能力。
所以 Midscene 不是为了把“点击”包装成 AI 能力,而是把“视觉定位”和“视觉状态判断”这两个麻烦点做成稳定能力。
pure-vision 是工程取舍,不是 Demo 话术
“只靠截图操作界面”听起来很像一个 Demo 卖点:看,不用 DOM 也能点按钮。
但对 Midscene 来说,pure-vision 更重要的意义是接口和职责更好划分。
如果 UI 操作依赖 DOM,Midscene 本质上还是 Web 自动化增强工具。如果 UI 操作依赖每个平台自己的控件树,每个平台都需要一套不同抽象。如果 UI 操作默认基于截图,那么 Web、移动端、桌面端、自定义设备就可以共享同一套操作语义。
这个选择带来的收益非常直接:
- 同一类 API 可以跨平台工作。
- 同一套 Agent 抽象可以接入不同设备。
- 同一份报告可以回放不同平台上的操作过程。
- 同一套 MCP / Skills 能力可以暴露给上层 Agent。
- 开发者不需要先理解目标平台的内部结构,才能开始写自动化。
这不是说平台差异消失了,而是平台差异被放在设备适配层处理:设备适配层负责截图和真实动作,上层 Agent 负责看图、理解目标、选择动作。
换句话说,Midscene 不是在做一个“会写 selector 的模型”,而是在做一个“能以用户视角操作界面”的系统。
DOM 仍然有位置,但不必默认优先
pure-vision 不等于排斥 DOM。
DOM 在很多场景里仍然有价值,尤其是这几类:
- 数据提取:读取图片链接、隐藏字段、结构化列表、不可见属性时,DOM 往往比截图更直接。
- 辅助理解:当视觉信息不足时,DOM 可以作为额外上下文,帮助模型理解页面。
- 性能优化:在 Web 场景中,如果某些元素结构稳定,可以用 XPath 等信息缓存已定位元素,减少重复模型调用。
- 确定性兜底:当某些业务已经有稳定 selector,完全可以把它作为确定性能力接进来。
所以问题不是“要不要 DOM”,而是“什么时候该用 DOM”。
Midscene 的取舍是:截图对应用户看到的界面,DOM 是可选上下文。二者都可以用,但 UI 操作的默认输入应该先尊重用户看到的界面。
这会改变自动化代码的组织方式
一旦 UI Agent 的核心变成“看见屏幕”,自动化代码的写法也会变化。
过去写自动化,开发者需要关心 selector 怎么写、XPath 会不会变、节点是否可见、等待哪个 DOM 状态、移动端控件树里哪个属性稳定。
在 Midscene 里,更自然的表达会变成:点击“登录按钮”、在“搜索框”输入关键词、判断页面是否展示了错误提示、提取当前列表里的商品名称、滚动到包含某条记录的位置。
这不是把工程问题全部交给自然语言。恰恰相反,它是在重新分工:
- 程序负责确定性的部分:流程、循环、分支、重试、超时、CI 集成、报告归档。
- 模型负责过去最依赖人眼的部分:看屏幕、找元素、读状态、理解视觉语义。
- Action Space 负责约束:Agent 可以选择动作,但动作集合、参数结构和真实执行由系统定义。
- Report 和 Cache 负责把过程留下来:过程可回放,稳定路径可复用。
这也是 Midscene 同时提供 JavaScript SDK、YAML、Playground、报告、缓存、MCP 和 Skills 的原因。一个能在项目里长期使用的 UI Agent,不应该只是一个“会操作页面的大模型”,而应该是一套可以被开发、调试、复用和集成的工具。
UI Agent 不应该只活在开发者工具里
我越来越觉得,UI Agent 和传统 UI 自动化最大的区别,不是“能不能用自然语言”。
更大的区别是:传统自动化更像是在操作界面的内部结构,UI Agent 更像是在操作用户看到的界面。
这会影响很多设计选择:要不要默认依赖 DOM?要不要支持跨平台?要不要支持 Canvas 和桌面?要不要记录截图、动作、模型调用和错误过程?要不要让上层 Agent 通过统一工具调用 UI?
Midscene 的很多设计,其实都围绕同一个判断展开:如果 Agent 要替用户操作,它就必须先看到用户看到的东西。
也就是屏幕。
一个简单判断标准
如果你的目标只是自动化一个结构稳定的 Web 页面,团队也能长期维护 selector,那么传统 DOM 自动化依然很好。它快、便宜、确定,也足够成熟。
但如果你的目标是跨 Web、移动端、桌面端操作,处理 Canvas、自绘控件、复杂视觉状态,让 Agent 操作真实用户看到的界面,把 UI 操作能力暴露给脚本、Playground、MCP、Skills,让自动化不再被某一种渲染技术绑定,那么 UI Agent 就必须先看见屏幕。
最后总结一下:
DOM 是开发者眼里的界面。截图是用户眼里的界面。
UI Agent 如果要替用户操作,就不能只活在开发者工具里。
为什么 Midscene 的 UI Agent 非得看见屏幕?

